package com.simon.springbootdemo.iot.control.plc;

import cn.hutool.core.util.StrUtil;
import com.iteaj.iot.consts.ExecStatus;
import com.iteaj.iot.plc.PlcClientProtocol;
import com.iteaj.iot.plc.omron.OmronConnectProperties;
import com.iteaj.iot.plc.omron.OmronTcpProtocol;
import com.iteaj.iot.plc.siemens.SiemensConnectProperties;
import com.iteaj.iot.plc.siemens.SiemensModel;
import com.iteaj.iot.plc.siemens.SiemensS7Protocol;
import com.iteaj.iot.utils.ByteUtil;
import com.simon.springbootdemo.iot.consts.IotConsts;
import com.simon.springbootdemo.iot.control.DebugCmd;
import com.simon.springbootdemo.iot.control.DebugHandle;
import com.simon.springbootdemo.iot.control.DebugResult;
import com.simon.springbootdemo.iot.control.DebugWebsocketWrite;
import com.simon.springbootdemo.iot.entity.IotDevice;
import com.simon.springbootdemo.iot.entity.IotPoint;
import com.simon.springbootdemo.iot.result.HttpResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Locale;

import static com.simon.springbootdemo.iot.consts.FieldTypeConsts.*;

@Component
@Slf4j
public class PlcDebugHandle implements DebugHandle<PlcDebugModel> {

    @Override
    public String type() {
        return "plc";
    }

    @Override
    public void handle(PlcDebugModel model, DebugWebsocketWrite write) {
        if (!StringUtils.hasText(model.getIp()) || model.getPort() == null) {
            write.write(HttpResult.Fail("未指定plc设备的ip地址和端口"));
            return;
        }

        PlcClientProtocol plcClientProtocol = null;
        if (model.getPlcType() == 2) {
            try {
                SiemensModel siemensModel = SiemensModel.valueOf(model.getModel());
                plcClientProtocol = new SiemensS7Protocol(new SiemensConnectProperties(model.getIp(), model.getPort(), siemensModel));
            } catch (Exception e) {
                e.printStackTrace();
                write.write(HttpResult.Fail("不支持西门子型号[" + model.getModel() + "]"));
                return;
            }
        } else if (model.getPlcType() == 3) {
            plcClientProtocol = new OmronTcpProtocol(new OmronConnectProperties(model.getIp(), model.getPort()));
        } else {
            write.write(HttpResult.Fail("不支持的plc类型[" + model.getPlcType() + "]"));
            return;
        }

        Object readValue = model.getValue();
        DebugResult result = new DebugResult(model).setDeviceSn(model.getDeviceSn())
                .setReqTime(System.currentTimeMillis());
        try {
            if (DebugCmd.READ.name().equals(model.getCmd().toUpperCase(Locale.ROOT))) {
                if (model.getType() == null) {
                    write.write(HttpResult.Fail("错误的数据类型[null]"));
                    return;
                }
                switch (model.getType()) {
                    case "bit":
                        readValue = plcClientProtocol.readBool(model.getAddress());
                        break;
                    case "short":
                        readValue = plcClientProtocol.readInt16(model.getAddress());
                        break;
                    case "int":
                        readValue = plcClientProtocol.readInt32(model.getAddress());
                        break;
                    case "long":
                        readValue = plcClientProtocol.readInt64(model.getAddress());
                        break;
                    case "float":
                        readValue = plcClientProtocol.readFloat(model.getAddress());
                        break;
                    case "double":
                        readValue = plcClientProtocol.readDouble(model.getAddress());
                        break;
                }
            } else if ("write".equals(model.getCmd())) {
                if (model.getType() == null) {
                    write.write(HttpResult.Fail("错误的数据类型[null]"));
                    return;
                }
                switch (model.getType()) {
                    case "bit":
                        // plcClientProtocol.write(model.getAddress(), true);
                        String pointValue = model.getValue();
                        String[] values = {"TRUE", "FALSE"};
                        if (StrUtil.isEmpty(pointValue)) {
                            pointValue =  values[1];
                        }
                        if (!Arrays.asList(values).contains(pointValue.toUpperCase())) {
                            pointValue =  values[1];
                        }
                        plcClientProtocol.write(model.getAddress(), Boolean.parseBoolean(pointValue.toLowerCase()));
                        break;
                    case "short":
                        plcClientProtocol.write(model.getAddress(), Short.parseShort(model.getValue()));
                        break;
                    case "int":
                        plcClientProtocol.write(model.getAddress(), Integer.parseInt(model.getValue()));
                        break;
                    case "long":
                        plcClientProtocol.write(model.getAddress(), Long.parseLong(model.getValue()));
                        break;
                    case "float":
                        plcClientProtocol.write(model.getAddress(), Float.parseFloat(model.getValue()));
                        break;
                    case "double":
                        plcClientProtocol.write(model.getAddress(), Double.parseDouble(model.getValue()));
                        break;
                }
            } else if ("message".equals(model.getCmd())) {
                if (StringUtils.hasText(model.getMessage())) {
                    byte[] bytes = ByteUtil.hexToByte(model.getMessage().trim());
                    byte[] readFull = plcClientProtocol.readFull(bytes);
                    readValue = readFull != null ? ByteUtil.bytesToHexByFormat(readFull) : "";
                } else {
                    write.write(HttpResult.Fail("请输入报文[" + model.getMessage() + "]"));
                    return;
                }
            } else {
                write.write(HttpResult.Fail("不支持的操作指令[" + model.getCmd() + "]"));
                return;
            }

            result.setValue(readValue).setRespTime(System.currentTimeMillis())
                    .setReqMsg(ByteUtil.bytesToHexByFormat(plcClientProtocol.requestMessage().getMessage()))
                    .setRespMsg(ByteUtil.bytesToHexByFormat(plcClientProtocol.responseMessage().getMessage()));
            ExecStatus execStatus = plcClientProtocol.getExecStatus();
            if (execStatus != ExecStatus.success) {
                write.write(HttpResult.StatusCode(result, execStatus.desc, 208));
            } else {
                write.write(HttpResult.Success(result));
            }

        } catch (Exception e) {
            write.write(HttpResult.Fail(e.getMessage()));
        }
    }



    @Override
    public HttpResult debug(IotDevice device, IotPoint point, DebugCmd cmd) {

        HttpResult result = null;

        if (!StringUtils.hasText(device.getDeviceIp()) || device.getDevicePort() == null) {
            return HttpResult.Fail("未指定plc设备的ip地址和端口");
        }

        // String isTaskDebug = sysConfigService.selectConfigByKey(IotConstants.PARAMS_KEY_TASK_DEBUG);
        // if (IotConstants.TRUE_STR.equalsIgnoreCase(isTaskDebug)) {
        //     device.setDeviceIp("127.0.0.1");
        //     device.setDevicePort(103);
        // }

        PlcClientProtocol plcClientProtocol = null;
        if (IotConsts.COLLECT_ACTION_PLC_SIEMENS.equals(device.getDeviceCategory())) {
            try {
                SiemensModel siemensModel = SiemensModel.valueOf(device.getDeviceModel());
                plcClientProtocol = new SiemensS7Protocol(new SiemensConnectProperties(device.getDeviceIp(), device.getDevicePort(), siemensModel));
            } catch (Exception e) {
                e.printStackTrace();
                return HttpResult.Fail("不支持西门子型号[" + device.getDeviceModel() + "]");
            }
        } else if (IotConsts.COLLECT_ACTION_PLC_OMRON.equals(device.getDeviceCategory())) {
            plcClientProtocol = new OmronTcpProtocol(new OmronConnectProperties(device.getDeviceIp(), device.getDevicePort()));
        } else {
            return HttpResult.Fail("不支持的plc类型[" + device.getDeviceCategory() + "]");
        }

        Object readValue = point.getPointValue();
        DebugResult res = new DebugResult(device, point).setDeviceSn(device.getDeviceSn())
                .setReqTime(System.currentTimeMillis());
        try {
            if (DebugCmd.READ.equals(cmd)) {
                if (point.getFieldType() == null) {
                    return HttpResult.Fail("错误的数据类型[null]");
                }
                switch (point.getFieldType()) {
                    case KEY_FIELD_TYPE_BIT:
                        readValue = plcClientProtocol.readBool(point.getPointAddress());
                        break;
                    case KEY_FIELD_TYPE_SHORT:
                        readValue = plcClientProtocol.readInt16(point.getPointAddress());
                        break;
                    case KEY_FIELD_TYPE_INT:
                        readValue = plcClientProtocol.readInt32(point.getPointAddress());
                        break;
                    case KEY_FIELD_TYPE_LONG:
                        readValue = plcClientProtocol.readInt64(point.getPointAddress());
                        break;
                    case KEY_FIELD_TYPE_FLOAT:
                        readValue = plcClientProtocol.readFloat(point.getPointAddress());
                        break;
                    case KEY_FIELD_TYPE_DOUBLE:
                        readValue = plcClientProtocol.readDouble(point.getPointAddress());
                        break;
                }
            } else if (DebugCmd.WRITE.equals(cmd)) {
                if (point.getFieldType() == null) {
                    return HttpResult.Fail("错误的数据类型[null]");
                }
                switch (point.getFieldType()) {
                    case KEY_FIELD_TYPE_BIT:
                        String pointValue = point.getPointValue();
                        String[] values = {"TRUE", "FALSE"};
                        if (StrUtil.isEmpty(pointValue)) {
                            pointValue =  values[1];
                        }
                        if (!Arrays.asList(values).contains(pointValue.toUpperCase())) {
                            pointValue =  values[1];
                        }
                        logger.error("布尔点位写入 : address: {}, expect: {}, actual: {}", point.getPointAddress(),
                                point.getPointValue(), pointValue.toLowerCase());
                        plcClientProtocol.write(point.getPointAddress(), Boolean.parseBoolean(pointValue.toLowerCase()));
                        break;
                    case KEY_FIELD_TYPE_SHORT:
                        plcClientProtocol.write(point.getPointAddress(), Short.parseShort(point.getPointValue()));
                        break;
                    case KEY_FIELD_TYPE_INT:
                        plcClientProtocol.write(point.getPointAddress(), Integer.parseInt(point.getPointValue()));
                        break;
                    case KEY_FIELD_TYPE_LONG:
                        plcClientProtocol.write(point.getPointAddress(), Long.parseLong(point.getPointValue()));
                        break;
                    case KEY_FIELD_TYPE_FLOAT:
                        plcClientProtocol.write(point.getPointAddress(), Float.parseFloat(point.getPointValue()));
                        break;
                    case KEY_FIELD_TYPE_DOUBLE:
                        plcClientProtocol.write(point.getPointAddress(), Double.parseDouble(point.getPointValue()));
                        break;
                }
            } else if (DebugCmd.MESSAGE.equals(cmd)) {
                if (StringUtils.hasText(point.getMessage())) {
                    byte[] bytes = ByteUtil.hexToByte(point.getMessage().trim());
                    byte[] readFull = plcClientProtocol.readFull(bytes);
                    readValue = readFull != null ? ByteUtil.bytesToHexByFormat(readFull) : "";
                } else {
                    return HttpResult.Fail("请输入报文[" + point.getMessage() + "]");
                }
            } else {
                return HttpResult.Fail("不支持的操作指令[" + cmd + "]");
            }

            res.setValue(readValue).setRespTime(System.currentTimeMillis())
                    .setReqMsg(ByteUtil.bytesToHexByFormat(plcClientProtocol.requestMessage().getMessage()))
                    .setRespMsg(ByteUtil.bytesToHexByFormat(plcClientProtocol.responseMessage().getMessage()));
            ExecStatus execStatus = plcClientProtocol.getExecStatus();
            if (execStatus != ExecStatus.success) {
                result = HttpResult.StatusCode(res, execStatus.desc, 208);
            } else {
                result = (HttpResult.Success(res));
            }

        } catch (Exception e) {
            result = HttpResult.Fail(e.getMessage());
        }
        print(device, point, res, cmd);
        return result;
    }

    public static void print(IotDevice device, IotPoint point, DebugResult res, DebugCmd cmd) {
        log.error("--------------------------------------------------------------------");
        log.error("cmd: {}, device: {} {}:{}  point: address: {}, value: {}", cmd.name(),
                device.getDeviceCategory(), device.getDeviceIp(), device.getDevicePort(),
                point.getPointAddress(), res.getValue());
        log.error("--------------------------------------------------------------------");
        log.error("request message: {}", res.getReqMsg());
        log.error("--------------------------------------------------------------------");
        log.error("response message: {}", res.getRespMsg());
        log.error("--------------------------------------------------------------------");

    }
}
